﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace DimensionsHelper.Services.DataCollector;

/**
* 建立行索引时，需要同时对多个关键字进行检索。
* 其中，类型名保存在节点中，类型值保存在单独的表中。
*
* Node
* |
* +------+------+---
* |      |      |    ...
* Val1   Val2   Val3
* |
* +------+------+
* |      |      |    ...
* Node1->Node2->Node3
* |
* +------+
* |      |  ...
* Val1   Val2
*/
[DebuggerDisplay("Key = {Key}, Value = {Value}")]
internal class Index<T> where T : class
{
    private readonly Dictionary<string, Index<T>> _children;

    public Index(string key, string value)
    {
        Key = key;
        Value = value;
        Previous = this;
        Next = this;
        _children = new Dictionary<string, Index<T>>();
    }

    /// <summary>
    ///     当前节点的父节点。
    /// </summary>
    public Index<T>? Parent { get; private set; }

    /// <summary>
    ///     索引节点的关键字。
    /// </summary>
    public string Key { get; }

    /// <summary>
    ///     索引节点关键字绑定的值。
    /// </summary>
    public string Value { get; }

    /// <summary>
    ///     当前节点保存的表格行。
    /// </summary>
    public T? Data { get; set; }

    /// <summary>
    ///     上一个节点
    /// </summary>
    private Index<T> Previous { get; set; }

    /// <summary>
    ///     下一个节点
    /// </summary>
    private Index<T> Next { get; set; }

    /// <summary>
    ///     如果当前节点包含任意一个子节点，将返回true。
    /// </summary>
    private bool HasChild => _children.Count > 0;


    /// <summary>
    ///     清空节点保存的内容。
    /// </summary>
    public void Clear()
    {
        Data = null;

        Previous = this;
        Next = this;

        _children.Clear();
    }


    private static void PushNode(Index<T> head, Index<T> node)
    {
        var last = head.Previous;
        last.Next = node;
        node.Previous = last;

        head.Previous = node;
        node.Next = head;
    }


    /// <summary>
    ///     获取第一个子节点，无论什么类型。如果当前节点没有子节点，将返回null。
    /// </summary>
    public Index<T>? GetFirstChild()
    {
        return _children.Count > 0 ? _children.First().Value : null;
    }

    /// <summary>
    ///     获取指定类型值的第一个子节点。
    /// </summary>
    /// <param name="key">查找的节点关键字</param>
    public Index<T>? GetFirstChild(string key)
    {
#if NET8_0_OR_GREATER
            return _children.GetValueOrDefault(key);
#else
        return _children.TryGetValue(key, out var value) ? value : null;
#endif
    }

    /// <summary>
    ///     获取指定类型值的子节点。
    /// </summary>
    /// <param name="key">当前类型的关键字</param>
    /// <param name="value">子节点关键字绑定的值</param>
    public Index<T> GetChild(string key, string value)
    {
        // 如果给定关键字不在子节点表中，添加新的并返回
        if (!_children.TryGetValue(key, out var node))
        {
            node = new Index<T>(key, value)
            {
                Parent = this
            };

            _children.Add(key, node);
            return node;
        }

        // 如果给定关键字已存在，查找是否已有给定值的节点
        // 如果已有，将其返回，否则，在链表末尾新建。

        var existNode = node.FindSibling(value);

        if (existNode != null)
        {
            return existNode;
        }

        var newNode = new Index<T>(key, value)
        {
            Parent = this
        };

        PushNode(node, newNode);

        return newNode;
    }

    /// <summary>
    ///     获取指定路径的子节点。
    /// </summary>
    /// <param name="path">路径列表，顺序不重要</param>
    public Index<T> GetChild(IEnumerable<KeyValuePair<string, string>> path)
    {
        var dict =
#if NET8_0_OR_GREATER
                new Dictionary<string, string>(path);
#else
            path.ToDictionary(e => e.Key, e => e.Value);
#endif
        // 查找插入节点
        var nodeToInsert = this;
        // 检查子节点类型是否存在于给定路径中
        if (nodeToInsert.HasChild)
        {
            var findNode = nodeToInsert;
            // 如果检索到了将移除 dict 中的关键字，并继续向下查找
            while ((findNode =
                       findNode.FindChild(n => dict.TryGetValue(n.Key, out var val) && val == n.Value)) != null)
            {
                dict.Remove(findNode.Key);
                nodeToInsert = findNode;
            }

            // 添加 dict 剩余的路径。
            return dict.Aggregate(nodeToInsert, (current, pair) => current.GetChild(pair.Key, pair.Value));
        }
        // 没有子节点，直接按顺序添加路径。
        return path.Aggregate(this, (current, pair) => current.GetChild(pair.Key, pair.Value));
    }

    /// <summary>
    ///     判断给定类型值是否在子节点表中。
    /// </summary>
    public bool Contains(string typeVal)
    {
        return _children.ContainsKey(typeVal);
    }


    /// <summary>
    ///     从此节点向后查找给定值的节点
    /// </summary>
    /// <param name="value">节点关键字绑定的值</param>
    /// <returns></returns>
    public Index<T>? FindSibling(string value)
    {
        return FindSibling(n => n.Value == value);
    }

    /// <summary>
    ///     寻找符合断言回调的兄弟节点。
    /// </summary>
    /// <param name="predicate">断言回调函数</param>
    /// <returns>查找到的节点，如果未找到，将返回null</returns>
    public Index<T>? FindSibling(Predicate<Index<T>> predicate)
    {
        var node = this;

        while (true)
        {
            if (predicate(node))
            {
                return node;
            }

            node = node.Next;

            if (node == this)
            {
                break;
            }
        }

        return null;
    }

    /// <summary>
    ///     查找符合断言回调的子节点。
    /// </summary>
    /// <param name="predicate">断言回调，参数是<see cref="Index{T}.Key" /></param>
    public Index<T>? FindChild(Predicate<Index<T>> predicate)
    {
        foreach (var node in _children.Values)
        {
            if (predicate(node))
            {
                return node;
            }

            var sibling = node.FindSibling(predicate);

            if (sibling != null)
            {
                return sibling;
            }
        }

        return null;
    }

    /// <summary>
    ///     从当前节点向下查找给定路径。
    /// </summary>
    public Index<T>? FindChild(IEnumerable<KeyValuePair<string, string>> path, bool onlyFindLeafNode)
    {
        var pathDict =
#if NET8_0_OR_GREATER
                new Dictionary<string, string>(path);
#else
            path.ToDictionary(e => e.Key, e => e.Value);
#endif
                
        var node = this;

        while (true)
        {
            // 各个值的子节点是链表
            var child = node.FindChild(n => pathDict.TryGetValue(n.Key, out var val) && val == n.Value);

            if (child != null)
            {
                pathDict.Remove(child.Key);
                node = child;
                continue;
            }

            return node == this || (onlyFindLeafNode && node.HasChild) ? null : node;
        }
    }
}